<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="刘清政">
  <meta name="keyword" content="hexo-theme">
  
    <link rel="shortcut icon" href="/css/images/logo.png">
  
  <title>
    
      7-Python异步库之asyncio | Justin-刘清政的博客
    
  </title>
  <link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/tomorrow.min.css" rel="stylesheet">
  
<link rel="stylesheet" href="/css/style.css">

  
    
<link rel="stylesheet" href="/css/plugins/gitment.css">

  
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
  
    
<script src="/js/qrious.js"></script>

  
  
    
<script src="/js/gitment.js"></script>

  
  

  
<meta name="generator" content="Hexo 4.2.0"></head>
<div class="wechat-share">
  <img src="/css/images/logo.png" />
</div>

  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>Justin-刘清政的博客</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">主页</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">标签</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">归档</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">关于我</a>
          
        </li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">主页</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">标签</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">归档</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">关于我</a>
            
          </li>
        
      </ul>
    </div>
  </div>
</header>

    <div id="article-banner">
  <h2>7-Python异步库之asyncio</h2>



  <p class="post-date">2020-03-25</p>
    <!-- 不蒜子统计 -->
    <span id="busuanzi_container_page_pv" style='display:none' class="">
        <i class="icon-smile icon"></i> 阅读数：<span id="busuanzi_value_page_pv"></span>次
    </span>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><h3 id="python-asyncio"><a href="#python-asyncio" class="headerlink" title="python asyncio"></a>python asyncio</h3><p>网络模型有很多中，为了实现高并发也有很多方案，多线程，多进程。无论多线程和多进程，IO的调度更多取决于系统，而协程的方式，调度来自用户，用户可以在函数中yield一个状态。使用协程可以实现高效的并发任务。Python的在3.4中引入了协程的概念，可是这个还是以生成器对象为基础，3.5则确定了协程的语法。下面将简单介绍asyncio的使用。实现协程的不仅仅是asyncio，tornado和gevent都实现了类似的功能。</p>
<ul>
<li>event_loop 事件循环：程序开启一个无限的循环，程序员会把一些函数注册到事件循环上。当满足事件发生的时候，调用相应的协程函数。</li>
<li>coroutine 协程：协程对象，指一个使用async关键字定义的函数，它的调用不会立即执行函数，而是会返回一个协程对象。协程对象需要注册到事件循环，由事件循环调用。</li>
<li>task  任务：一个协程对象就是一个原生可以挂起的函数，任务则是对协程进一步封装，其中包含任务的各种状态。</li>
<li>future： 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别</li>
<li>async/await 关键字：python3.5 用于定义协程的关键字，async定义一个协程，await用于挂起阻塞的异步调用接口。</li>
</ul>
<p>上述的概念单独拎出来都不好懂，比较他们之间是相互联系，一起工作。下面看例子，再回溯上述概念，更利于理解。</p>
<h3 id="定义一个协程"><a href="#定义一个协程" class="headerlink" title="定义一个协程"></a>定义一个协程</h3><p>定义一个协程很简单，使用async关键字，就像定义普通函数一样：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span> : time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">coroutine = do_some_work(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">loop.run_until_complete(coroutine)</span><br><span class="line"></span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>通过async关键字定义一个协程（coroutine），协程也是一种对象。协程不能直接运行，需要把协程加入到事件循环（loop），由后者在适当的时候调用协程。<code>asyncio.get_event_loop</code>方法可以创建一个事件循环，然后使用<code>run_until_complete</code>将协程注册到事件循环，并启动事件循环。因为本例只有一个协程，于是可以看见如下输出：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">Waiting</span>:  2</span><br><span class="line"><span class="selector-tag">TIME</span>:  0<span class="selector-class">.0004658699035644531</span></span><br></pre></td></tr></table></figure>

<h3 id="创建一个task"><a href="#创建一个task" class="headerlink" title="创建一个task"></a>创建一个task</h3><p>协程对象不能直接运行，在注册事件循环的时候，其实是run_until_complete方法将协程包装成为了一个任务（task）对象。所谓task对象是Future类的子类。保存了协程运行后的状态，用于未来获取协程的结果。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span> : time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">coroutine = do_some_work(<span class="number">2</span>)</span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line"><span class="comment"># task = asyncio.ensure_future(coroutine)</span></span><br><span class="line">task = loop.create_task(coroutine)</span><br><span class="line">print(task)</span><br><span class="line">loop.run_until_complete(task)</span><br><span class="line">print(task)</span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>可以看到输出结果为：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;Task pending coro=&lt;do_some_work() running at /Users/ghost/Rsj217/python3<span class="number">.6</span>/<span class="keyword">async</span>/<span class="keyword">async</span>-main.py:<span class="number">17</span>&gt;&gt;</span><br><span class="line">Waiting:  <span class="number">2</span></span><br><span class="line">&lt;Task finished coro=&lt;do_some_work() done, defined at /Users/ghost/Rsj217/python3<span class="number">.6</span>/<span class="keyword">async</span>/<span class="keyword">async</span>-main.py:<span class="number">17</span>&gt; result=<span class="literal">None</span>&gt;</span><br><span class="line">TIME:  <span class="number">0.0003490447998046875</span></span><br></pre></td></tr></table></figure>

<p>创建task后，task在加入事件循环之前是pending状态，因为do_some_work中没有耗时的阻塞操作，task很快就执行完毕了。后面打印的finished状态。</p>
<p>asyncio.ensure_future(coroutine) 和 loop.create_task(coroutine)都可以创建一个task，run_until_complete的参数是一个futrue对象。当传入一个协程，其内部会自动封装成task，task是Future的子类。<code>isinstance(task, asyncio.Future)</code>将会输出True。</p>
<h3 id="绑定回调"><a href="#绑定回调" class="headerlink" title="绑定回调"></a>绑定回调</h3><p>绑定回调，在task执行完毕的时候可以获取执行的结果，回调的最后一个参数是future对象，通过该对象可以获取协程返回值。如果回调需要多个参数，可以通过偏函数导入。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span> : time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">callback</span><span class="params">(future)</span>:</span></span><br><span class="line">    print(<span class="string">'Callback: '</span>, future.result())</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">coroutine = do_some_work(<span class="number">2</span>)</span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">task = asyncio.ensure_future(coroutine)</span><br><span class="line">task.add_done_callback(callback)</span><br><span class="line">loop.run_until_complete(task)</span><br><span class="line"></span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>



<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">callback</span><span class="params">(t, future)</span>:</span></span><br><span class="line">    print(<span class="string">'Callback:'</span>, t, future.result())</span><br><span class="line"></span><br><span class="line">task.add_done_callback(functools.partial(callback, <span class="number">2</span>))</span><br></pre></td></tr></table></figure>

<p>可以看到，coroutine执行结束时候会调用回调函数。并通过参数future获取协程执行的结果。我们创建的task和回调里的future对象，实际上是同一个对象。</p>
<h3 id="future-与-result"><a href="#future-与-result" class="headerlink" title="future 与 result"></a>future 与 result</h3><p>回调一直是很多异步编程的恶梦，程序员更喜欢使用同步的编写方式写异步代码，以避免回调的恶梦。回调中我们使用了future对象的result方法。前面不绑定回调的例子中，我们可以看到task有fiinished状态。在那个时候，可以直接读取task的result方法。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting &#123;&#125;'</span>.format(x))</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">coroutine = do_some_work(<span class="number">2</span>)</span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">task = asyncio.ensure_future(coroutine)</span><br><span class="line">loop.run_until_complete(task)</span><br><span class="line"></span><br><span class="line">print(<span class="string">'Task ret: &#123;&#125;'</span>.format(task.result()))</span><br><span class="line">print(<span class="string">'TIME: &#123;&#125;'</span>.format(now() - start))</span><br></pre></td></tr></table></figure>

<p>可以看到输出的结果：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">Waiting</span>:  2</span><br><span class="line"><span class="selector-tag">Task</span> <span class="selector-tag">ret</span>:  <span class="selector-tag">Done</span> <span class="selector-tag">after</span> 2<span class="selector-tag">s</span></span><br><span class="line"><span class="selector-tag">TIME</span>:  0<span class="selector-class">.0003650188446044922</span></span><br></pre></td></tr></table></figure>

<h3 id="阻塞和await"><a href="#阻塞和await" class="headerlink" title="阻塞和await"></a>阻塞和await</h3><p>使用async可以定义协程对象，使用await可以针对耗时的操作进行挂起，就像生成器里的yield一样，函数让出控制权。协程遇到await，事件循环将会挂起该协程，执行别的协程，直到其他的协程也挂起或者执行完毕，再进行下一个协程的执行。</p>
<p>耗时的操作一般是一些IO操作，例如网络请求，文件读取等。我们使用asyncio.sleep函数来模拟IO操作。协程的目的也是让这些IO操作异步化。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span>: time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">coroutine = do_some_work(<span class="number">2</span>)</span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">task = asyncio.ensure_future(coroutine)</span><br><span class="line">loop.run_until_complete(task)</span><br><span class="line"></span><br><span class="line">print(<span class="string">'Task ret: '</span>, task.result())</span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>在 sleep的时候，使用await让出控制权。即当遇到阻塞调用的函数的时候，使用await方法将协程的控制权让出，以便loop调用其他的协程。现在我们的例子就用耗时的阻塞操作了。</p>
<h3 id="并发和并行"><a href="#并发和并行" class="headerlink" title="并发和并行"></a>并发和并行</h3><p>并发和并行一直是容易混淆的概念。并发通常指有多个任务需要同时进行，并行则是同一时刻有多个任务执行。用上课来举例就是，并发情况下是一个老师在同一时间段辅助不同的人功课。并行则是好几个老师分别同时辅助多个学生功课。简而言之就是一个人同时吃三个馒头还是三个人同时分别吃一个的情况，吃一个馒头算一个任务。</p>
<p>asyncio实现并发，就需要多个协程来完成任务，每当有任务阻塞的时候就await，然后其他协程继续工作。创建多个协程的列表，然后将这些协程注册到事件循环中。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span>: time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">coroutine3 = do_some_work(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">tasks = [</span><br><span class="line">    asyncio.ensure_future(coroutine1),</span><br><span class="line">    asyncio.ensure_future(coroutine2),</span><br><span class="line">    asyncio.ensure_future(coroutine3)</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">loop.run_until_complete(asyncio.wait(tasks))</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> task <span class="keyword">in</span> tasks:</span><br><span class="line">    print(<span class="string">'Task ret: '</span>, task.result())</span><br><span class="line"></span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>结果如下</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">Waiting</span>:  1</span><br><span class="line"><span class="selector-tag">Waiting</span>:  2</span><br><span class="line"><span class="selector-tag">Waiting</span>:  4</span><br><span class="line"><span class="selector-tag">Task</span> <span class="selector-tag">ret</span>:  <span class="selector-tag">Done</span> <span class="selector-tag">after</span> 1<span class="selector-tag">s</span></span><br><span class="line"><span class="selector-tag">Task</span> <span class="selector-tag">ret</span>:  <span class="selector-tag">Done</span> <span class="selector-tag">after</span> 2<span class="selector-tag">s</span></span><br><span class="line"><span class="selector-tag">Task</span> <span class="selector-tag">ret</span>:  <span class="selector-tag">Done</span> <span class="selector-tag">after</span> 4<span class="selector-tag">s</span></span><br><span class="line"><span class="selector-tag">TIME</span>:  4<span class="selector-class">.003541946411133</span></span><br></pre></td></tr></table></figure>

<p>总时间为4s左右。4s的阻塞时间，足够前面两个协程执行完毕。如果是同步顺序的任务，那么至少需要7s。此时我们使用了aysncio实现了并发。asyncio.wait(tasks) 也可以使用 asyncio.gather(*tasks) ,前者接受一个task列表，后者接收一堆task。</p>
<h3 id="协程嵌套"><a href="#协程嵌套" class="headerlink" title="协程嵌套"></a>协程嵌套</h3><p>使用async可以定义协程，协程用于耗时的io操作，我们也可以封装更多的io操作过程，这样就实现了嵌套的协程，即一个协程中await了另外一个协程，如此连接起来。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span>: time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">    coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">    coroutine3 = do_some_work(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">    tasks = [</span><br><span class="line">        asyncio.ensure_future(coroutine1),</span><br><span class="line">        asyncio.ensure_future(coroutine2),</span><br><span class="line">        asyncio.ensure_future(coroutine3)</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    dones, pendings = <span class="keyword">await</span> asyncio.wait(tasks)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> task <span class="keyword">in</span> dones:</span><br><span class="line">        print(<span class="string">'Task ret: '</span>, task.result())</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">loop.run_until_complete(main())</span><br><span class="line"></span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>如果使用的是 asyncio.gather创建协程对象，那么await的返回值就是协程运行的结果。</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">results = <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> result <span class="keyword">in</span> results:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">'Task ret: '</span>, result)</span><br></pre></td></tr></table></figure>

<p>不在main协程函数里处理结果，直接返回await的内容，那么最外层的run_until_complete将会返回main协程的结果。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">    coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">    coroutine3 = do_some_work(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    tasks = [</span><br><span class="line">        asyncio.ensure_future(coroutine1),</span><br><span class="line">        asyncio.ensure_future(coroutine2),</span><br><span class="line">        asyncio.ensure_future(coroutine3)</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">results = loop.run_until_complete(main())</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> result <span class="keyword">in</span> results:</span><br><span class="line">    print(<span class="string">'Task ret: '</span>, result)</span><br></pre></td></tr></table></figure>

<p>或者返回使用asyncio.wait方式挂起协程。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">    coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">    coroutine3 = do_some_work(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">    tasks = [</span><br><span class="line">        asyncio.ensure_future(coroutine1),</span><br><span class="line">        asyncio.ensure_future(coroutine2),</span><br><span class="line">        asyncio.ensure_future(coroutine3)</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> asyncio.wait(tasks)</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">done, pending = loop.run_until_complete(main())</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> task <span class="keyword">in</span> done:</span><br><span class="line">    print(<span class="string">'Task ret: '</span>, task.result())</span><br></pre></td></tr></table></figure>

<p>也可以使用asyncio的as_completed方法</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">    coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">    coroutine3 = do_some_work(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">    tasks = [</span><br><span class="line">        asyncio.ensure_future(coroutine1),</span><br><span class="line">        asyncio.ensure_future(coroutine2),</span><br><span class="line">        asyncio.ensure_future(coroutine3)</span><br><span class="line">    ]</span><br><span class="line">    <span class="keyword">for</span> task <span class="keyword">in</span> asyncio.as_completed(tasks):</span><br><span class="line">        result = <span class="keyword">await</span> task</span><br><span class="line">        print(<span class="string">'Task ret: &#123;&#125;'</span>.format(result))</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">done = loop.run_until_complete(main())</span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>由此可见，协程的调用和组合十分灵活，尤其是对于结果的处理，如何返回，如何挂起，需要逐渐积累经验和前瞻的设计。</p>
<h3 id="协程停止"><a href="#协程停止" class="headerlink" title="协程停止"></a>协程停止</h3><p>上面见识了协程的几种常用的用法，都是协程围绕着事件循环进行的操作。future对象有几个状态：</p>
<ul>
<li>Pending</li>
<li>Running</li>
<li>Done</li>
<li>Cancelled</li>
</ul>
<p>创建future的时候，task为pending，事件循环调用执行的时候当然就是running，调用完毕自然就是done，如果需要停止事件循环，就需要先把task取消。可以使用asyncio.Task获取事件循环的task</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span>: time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line">coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">coroutine3 = do_some_work(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">tasks = [</span><br><span class="line">    asyncio.ensure_future(coroutine1),</span><br><span class="line">    asyncio.ensure_future(coroutine2),</span><br><span class="line">    asyncio.ensure_future(coroutine3)</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    loop.run_until_complete(asyncio.wait(tasks))</span><br><span class="line"><span class="keyword">except</span> KeyboardInterrupt <span class="keyword">as</span> e:</span><br><span class="line">    print(asyncio.Task.all_tasks())</span><br><span class="line">    <span class="keyword">for</span> task <span class="keyword">in</span> asyncio.Task.all_tasks():</span><br><span class="line">        print(task.cancel())</span><br><span class="line">    loop.stop()</span><br><span class="line">    loop.run_forever()</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    loop.close()</span><br><span class="line"></span><br><span class="line">print(<span class="string">'TIME: '</span>, now() - start)</span><br></pre></td></tr></table></figure>

<p>启动事件循环之后，马上ctrl+c，会触发run_until_complete的执行异常 KeyBorardInterrupt。然后通过循环asyncio.Task取消future。可以看到输出如下：</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Waiting:  <span class="number">1</span></span><br><span class="line">Waiting:  <span class="number">2</span></span><br><span class="line">Waiting:  <span class="number">2</span></span><br><span class="line">&#123;&lt;Task pending coro=&lt;do_some_work() running at /Users/ghost/Rsj217/python3<span class="number">.6</span>/<span class="keyword">async</span>/<span class="keyword">async</span>-main.py:<span class="number">18</span>&gt; wait_for=&lt;Future pending cb=[&lt;TaskWakeupMethWrapper <span class="keyword">object</span> at <span class="number">0x101230648</span>&gt;()]&gt; cb=[_wait.&lt;locals&gt;._on_completion() at /Library/Frameworks/Python.framework/Versions/<span class="number">3.6</span>/lib/python3<span class="number">.6</span>/asyncio/tasks.py:<span class="number">374</span>]&gt;, &lt;Task pending coro=&lt;do_some_work() running at /Users/ghost/Rsj217/python3<span class="number">.6</span>/<span class="keyword">async</span>/<span class="keyword">async</span>-main.py:<span class="number">18</span>&gt; wait_for=&lt;Future pending cb=[&lt;TaskWakeupMethWrapper <span class="keyword">object</span> at <span class="number">0x1032b10a8</span>&gt;()]&gt; cb=[_wait.&lt;locals&gt;._on_completion() at /Library/Frameworks/Python.framework/Versions/<span class="number">3.6</span>/lib/python3<span class="number">.6</span>/asyncio/tasks.py:<span class="number">374</span>]&gt;, &lt;Task pending coro=&lt;wait() running at /Library/Frameworks/Python.framework/Versions/<span class="number">3.6</span>/lib/python3<span class="number">.6</span>/asyncio/tasks.py:<span class="number">307</span>&gt; wait_for=&lt;Future pending cb=[&lt;TaskWakeupMethWrapper <span class="keyword">object</span> at <span class="number">0x103317d38</span>&gt;()]&gt; cb=[_run_until_complete_cb() at /Library/Frameworks/Python.framework/Versions/<span class="number">3.6</span>/lib/python3<span class="number">.6</span>/asyncio/base_events.py:<span class="number">176</span>]&gt;, &lt;Task pending coro=&lt;do_some_work() running at /Users/ghost/Rsj217/python3<span class="number">.6</span>/<span class="keyword">async</span>/<span class="keyword">async</span>-main.py:<span class="number">18</span>&gt; wait_for=&lt;Future pending cb=[&lt;TaskWakeupMethWrapper <span class="keyword">object</span> at <span class="number">0x103317be8</span>&gt;()]&gt; cb=[_wait.&lt;locals&gt;._on_completion() at /Library/Frameworks/Python.framework/Versions/<span class="number">3.6</span>/lib/python3<span class="number">.6</span>/asyncio/tasks.py:<span class="number">374</span>]&gt;&#125;</span><br><span class="line">True</span><br><span class="line">True</span><br><span class="line">True</span><br><span class="line">True</span><br><span class="line">TIME:  <span class="number">0.8858370780944824</span></span><br></pre></td></tr></table></figure>

<p>True表示cannel成功，loop stop之后还需要再次开启事件循环，最后在close，不然还会抛出异常：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Task was destroyed but it is pending!</span><br><span class="line">task: &lt;Task pending coro=&lt;do_some_work() <span class="keyword">done</span>,</span><br></pre></td></tr></table></figure>

<p>循环task，逐个cancel是一种方案，可是正如上面我们把task的列表封装在main函数中，main函数外进行事件循环的调用。这个时候，main相当于最外出的一个task，那么处理包装的main函数即可。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span>: time.time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting: '</span>, x)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Done after &#123;&#125;s'</span>.format(x)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    coroutine1 = do_some_work(<span class="number">1</span>)</span><br><span class="line">    coroutine2 = do_some_work(<span class="number">2</span>)</span><br><span class="line">    coroutine3 = do_some_work(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    tasks = [</span><br><span class="line">        asyncio.ensure_future(coroutine1),</span><br><span class="line">        asyncio.ensure_future(coroutine2),</span><br><span class="line">        asyncio.ensure_future(coroutine3)</span><br><span class="line">    ]</span><br><span class="line">    done, pending = <span class="keyword">await</span> asyncio.wait(tasks)</span><br><span class="line">    <span class="keyword">for</span> task <span class="keyword">in</span> done:</span><br><span class="line">        print(<span class="string">'Task ret: '</span>, task.result())</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line"></span><br><span class="line">loop = asyncio.get_event_loop()</span><br><span class="line">task = asyncio.ensure_future(main())</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    loop.run_until_complete(task)</span><br><span class="line"><span class="keyword">except</span> KeyboardInterrupt <span class="keyword">as</span> e:</span><br><span class="line">    print(asyncio.Task.all_tasks())</span><br><span class="line">    print(asyncio.gather(*asyncio.Task.all_tasks()).cancel())</span><br><span class="line">    loop.stop()</span><br><span class="line">    loop.run_forever()</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    loop.close()</span><br></pre></td></tr></table></figure>

<h3 id="不同线程的事件循环"><a href="#不同线程的事件循环" class="headerlink" title="不同线程的事件循环"></a>不同线程的事件循环</h3><p>很多时候，我们的事件循环用于注册协程，而有的协程需要动态的添加到事件循环中。一个简单的方式就是使用多线程。当前线程创建一个事件循环，然后在新建一个线程，在新线程中启动事件循环。当前线程不会被block。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Thread</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">start_loop</span><span class="params">(loop)</span>:</span></span><br><span class="line">    asyncio.set_event_loop(loop)</span><br><span class="line">    loop.run_forever()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">more_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'More work &#123;&#125;'</span>.format(x))</span><br><span class="line">    time.sleep(x)</span><br><span class="line">    print(<span class="string">'Finished more work &#123;&#125;'</span>.format(x))</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line">new_loop = asyncio.new_event_loop()</span><br><span class="line">t = Thread(target=start_loop, args=(new_loop,))</span><br><span class="line">t.start()</span><br><span class="line">print(<span class="string">'TIME: &#123;&#125;'</span>.format(time.time() - start))</span><br><span class="line"></span><br><span class="line">new_loop.call_soon_threadsafe(more_work, <span class="number">6</span>)</span><br><span class="line">new_loop.call_soon_threadsafe(more_work, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>

<p>启动上述代码之后，当前线程不会被block，新线程中会按照顺序执行call_soon_threadsafe方法注册的more_work方法，后者因为time.sleep操作是同步阻塞的，因此运行完毕more_work需要大致6 + 3</p>
<h3 id="新线程协程"><a href="#新线程协程" class="headerlink" title="新线程协程"></a>新线程协程</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">start_loop</span><span class="params">(loop)</span>:</span></span><br><span class="line">    asyncio.set_event_loop(loop)</span><br><span class="line">    loop.run_forever()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting &#123;&#125;'</span>.format(x))</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(x)</span><br><span class="line">    print(<span class="string">'Done after &#123;&#125;s'</span>.format(x))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">more_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'More work &#123;&#125;'</span>.format(x))</span><br><span class="line">    time.sleep(x)</span><br><span class="line">    print(<span class="string">'Finished more work &#123;&#125;'</span>.format(x))</span><br><span class="line"></span><br><span class="line">start = now()</span><br><span class="line">new_loop = asyncio.new_event_loop()</span><br><span class="line">t = Thread(target=start_loop, args=(new_loop,))</span><br><span class="line">t.start()</span><br><span class="line">print(<span class="string">'TIME: &#123;&#125;'</span>.format(time.time() - start))</span><br><span class="line"></span><br><span class="line">asyncio.run_coroutine_threadsafe(do_some_work(<span class="number">6</span>), new_loop)</span><br><span class="line">asyncio.run_coroutine_threadsafe(do_some_work(<span class="number">4</span>), new_loop)</span><br></pre></td></tr></table></figure>

<p>上述的例子，主线程中创建一个new_loop，然后在另外的子线程中开启一个无限事件循环。主线程通过run_coroutine_threadsafe新注册协程对象。这样就能在子线程中进行事件循环的并发操作，同时主线程又不会被block。一共执行的时间大概在6s左右。</p>
<h3 id="master-worker主从模式"><a href="#master-worker主从模式" class="headerlink" title="master-worker主从模式"></a>master-worker主从模式</h3><p>对于并发任务，通常是用生成消费模型，对队列的处理可以使用类似master-worker的方式，master主要用户获取队列的msg，worker用户处理消息。</p>
<p>为了简单起见，并且协程更适合单线程的方式，我们的主线程用来监听队列，子线程用于处理队列。这里使用redis的队列。主线程中有一个是无限循环，用户消费队列。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> True:</span><br><span class="line">    task = rcon.rpop(<span class="string">"queue"</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> task:</span><br><span class="line">        time.sleep(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    asyncio.run_coroutine_threadsafe(do_some_work(<span class="keyword">int</span>(task)), new_loop)</span><br></pre></td></tr></table></figure>

<p>给队列添加一些数据：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 2</span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 5</span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 1</span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 1</span><br></pre></td></tr></table></figure>

<p>可以看见输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Waiting  2</span><br><span class="line">Done 2</span><br><span class="line">Waiting  5</span><br><span class="line">Waiting  1</span><br><span class="line">Done 1</span><br><span class="line">Waiting  1</span><br><span class="line">Done 1</span><br><span class="line">Done 5</span><br></pre></td></tr></table></figure>

<p>我们发起了一个耗时5s的操作，然后又发起了连个1s的操作，可以看见子线程并发的执行了这几个任务，其中5s awati的时候，相继执行了1s的两个任务。</p>
<h3 id="停止子线程"><a href="#停止子线程" class="headerlink" title="停止子线程"></a>停止子线程</h3><p>如果一切正常，那么上面的例子很完美。可是，需要停止程序，直接ctrl+c，会抛出KeyboardInterrupt错误，我们修改一下主循环：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        task = rcon.rpop(<span class="string">"queue"</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> task:</span><br><span class="line">            time.sleep(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        asyncio.run_coroutine_threadsafe(do_some_work(int(task)), new_loop)</span><br><span class="line"><span class="keyword">except</span> KeyboardInterrupt <span class="keyword">as</span> e:</span><br><span class="line">    print(e)</span><br><span class="line">    new_loop.stop()</span><br></pre></td></tr></table></figure>

<p>可是实际上并不好使，虽然主线程try了KeyboardInterrupt异常，但是子线程并没有退出，为了解决这个问题，可以设置子线程为守护线程，这样当主线程结束的时候，子线程也随机退出。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">new_loop = asyncio.new_event_loop()</span><br><span class="line">t = Thread(target=start_loop, args=(new_loop,))</span><br><span class="line">t.setDaemon(<span class="literal">True</span>)    <span class="comment"># 设置子线程为守护线程</span></span><br><span class="line">t.start()</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="comment"># print('start rpop')</span></span><br><span class="line">        task = rcon.rpop(<span class="string">"queue"</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> task:</span><br><span class="line">            time.sleep(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        asyncio.run_coroutine_threadsafe(do_some_work(int(task)), new_loop)</span><br><span class="line"><span class="keyword">except</span> KeyboardInterrupt <span class="keyword">as</span> e:</span><br><span class="line">    print(e)</span><br><span class="line">    new_loop.stop()</span><br></pre></td></tr></table></figure>

<p>线程停止程序的时候，主线程退出后，子线程也随机退出才了，并且停止了子线程的协程任务。</p>
<h3 id="aiohttp"><a href="#aiohttp" class="headerlink" title="aiohttp"></a>aiohttp</h3><p>在消费队列的时候，我们使用asyncio的sleep用于模拟耗时的io操作。以前有一个短信服务，需要在协程中请求远程的短信api，此时需要是需要使用aiohttp进行异步的http请求。大致代码如下：</p>
<p>server.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/&lt;int:x&gt;')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">index</span><span class="params">(x)</span>:</span></span><br><span class="line">    time.sleep(x)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"&#123;&#125; It works"</span>.format(x)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/error')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">error</span><span class="params">()</span>:</span></span><br><span class="line">    time.sleep(<span class="number">3</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"error!"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    app.run(debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure>

<p><code>/</code>接口表示短信接口，<code>/error</code>表示请求<code>/</code>失败之后的报警。</p>
<p>async-custoimer.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Thread</span><br><span class="line"><span class="keyword">import</span> redis</span><br><span class="line"><span class="keyword">import</span> aiohttp</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_redis</span><span class="params">()</span>:</span></span><br><span class="line">    connection_pool = redis.ConnectionPool(host=<span class="string">'127.0.0.1'</span>, db=<span class="number">3</span>)</span><br><span class="line">    <span class="keyword">return</span> redis.Redis(connection_pool=connection_pool)</span><br><span class="line"></span><br><span class="line">rcon = get_redis()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">start_loop</span><span class="params">(loop)</span>:</span></span><br><span class="line">    asyncio.set_event_loop(loop)</span><br><span class="line">    loop.run_forever()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">fetch</span><span class="params">(url)</span>:</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> aiohttp.ClientSession() <span class="keyword">as</span> session:</span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">with</span> session.get(url) <span class="keyword">as</span> resp:</span><br><span class="line">            print(resp.status)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">await</span> resp.text()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">do_some_work</span><span class="params">(x)</span>:</span></span><br><span class="line">    print(<span class="string">'Waiting '</span>, x)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        ret = <span class="keyword">await</span> fetch(url=<span class="string">'http://127.0.0.1:5000/&#123;&#125;'</span>.format(x))</span><br><span class="line">        print(ret)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            print(<span class="keyword">await</span> fetch(url=<span class="string">'http://127.0.0.1:5000/error'</span>))</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            print(e)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        print(<span class="string">'Done &#123;&#125;'</span>.format(x))</span><br><span class="line"></span><br><span class="line">new_loop = asyncio.new_event_loop()</span><br><span class="line">t = Thread(target=start_loop, args=(new_loop,))</span><br><span class="line">t.setDaemon(<span class="literal">True</span>)</span><br><span class="line">t.start()</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        task = rcon.rpop(<span class="string">"queue"</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> task:</span><br><span class="line">            time.sleep(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        asyncio.run_coroutine_threadsafe(do_some_work(int(task)), new_loop)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    print(<span class="string">'error'</span>)</span><br><span class="line">    new_loop.stop()</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure>

<p>有一个问题需要注意，我们在fetch的时候try了异常，如果没有try这个异常，即使发生了异常，子线程的事件循环也不会退出。主线程也不会退出，暂时没找到办法可以把子线程的异常raise传播到主线程。（如果谁找到了比较好的方式，希望可以带带我）。</p>
<p>对于redis的消费，还有一个block的方法：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        _, task = rcon.brpop(<span class="string">"queue"</span>)</span><br><span class="line">        asyncio.run_coroutine_threadsafe(do_some_work(int(task)), new_loop)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    print(<span class="string">'error'</span>, e)</span><br><span class="line">    new_loop.stop()</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure>

<p>使用 brpop方法，会block住task，如果主线程有消息，才会消费。测试了一下，似乎brpop的方式更适合这种队列消费的模型。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 5</span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 1</span><br><span class="line">(<span class="selector-tag">integer</span>) 1</span><br><span class="line">127<span class="selector-class">.0</span><span class="selector-class">.0</span><span class="selector-class">.1</span><span class="selector-pseudo">:6379</span><span class="selector-attr">[3]</span>&gt; <span class="selector-tag">lpush</span> <span class="selector-tag">queue</span> 1</span><br></pre></td></tr></table></figure>

<p>可以看到结果</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Waiting  5</span><br><span class="line">Waiting  1</span><br><span class="line">Waiting  1</span><br><span class="line">200</span><br><span class="line">1 It works</span><br><span class="line">Done 1</span><br><span class="line">200</span><br><span class="line">1 It works</span><br><span class="line">Done 1</span><br><span class="line">200</span><br><span class="line">5 It works</span><br><span class="line">Done 5</span><br></pre></td></tr></table></figure>

<h3 id="协程消费"><a href="#协程消费" class="headerlink" title="协程消费"></a>协程消费</h3><p>主线程用于监听队列，然后子线程的做事件循环的worker是一种方式。还有一种方式实现这种类似master-worker的方案。即把监听队列的无限循环逻辑一道协程中。程序初始化就创建若干个协程，实现类似并行的效果。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> redis</span><br><span class="line"></span><br><span class="line">now = <span class="keyword">lambda</span> : time.time()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_redis</span><span class="params">()</span>:</span></span><br><span class="line">    connection_pool = redis.ConnectionPool(host=<span class="string">'127.0.0.1'</span>, db=<span class="number">3</span>)</span><br><span class="line">    <span class="keyword">return</span> redis.Redis(connection_pool=connection_pool)</span><br><span class="line"></span><br><span class="line">rcon = get_redis()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">worker</span><span class="params">()</span>:</span></span><br><span class="line">    print(<span class="string">'Start worker'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        start = now()</span><br><span class="line">        task = rcon.rpop(<span class="string">"queue"</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> task:</span><br><span class="line">            <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        print(<span class="string">'Wait '</span>, int(task))</span><br><span class="line">        <span class="keyword">await</span> asyncio.sleep(int(task))</span><br><span class="line">        print(<span class="string">'Done '</span>, task, now() - start)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    asyncio.ensure_future(worker())</span><br><span class="line">    asyncio.ensure_future(worker())</span><br><span class="line"></span><br><span class="line">    loop = asyncio.get_event_loop()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        loop.run_forever()</span><br><span class="line">    <span class="keyword">except</span> KeyboardInterrupt <span class="keyword">as</span> e:</span><br><span class="line">        print(asyncio.gather(*asyncio.Task.all_tasks()).cancel())</span><br><span class="line">        loop.stop()</span><br><span class="line">        loop.run_forever()</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        loop.close()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>

<p>这样做就可以多多启动几个worker来监听队列。一样可以到达效果。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>上述简单的介绍了asyncio的用法，主要是理解事件循环，协程和任务，future的关系。异步编程不同于常见的同步编程，设计程序的执行流的时候，需要特别的注意。毕竟这和以往的编码经验有点不一样。可是仔细想想，我们平时处事的时候，大脑会自然而然的实现异步协程。比如等待煮茶的时候，可以多写几行代码。</p>
</section>
    <!-- Tags START -->
    
      <div class="tags">
        <span>Tags:</span>
        
  <a href="/tags#Python" >
    <span class="tag-code">Python</span>
  </a>

      </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/python/Python%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/15-%E5%86%85%E5%AD%98%E4%B8%AD%E7%9A%84%E7%BA%BF%E7%A8%8B/">
        <span class="nav-arrow">← </span>
        
          1-Python中的GIL
        
      </a>
    
    
      <a class="nav-right" href="/python/Python%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/13-%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB/">
        
          1-Python中的GIL
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
      <div class="money-like">
        <div class="reward-btn">
          赏
          <span class="money-code">
            <span class="alipay-code">
              <div class="code-image"></div>
              <b>使用支付宝打赏</b>
            </span>
            <span class="wechat-code">
              <div class="code-image"></div>
              <b>使用微信打赏</b>
            </span>
          </span>
        </div>
        <p class="notice">点击上方按钮,请我喝杯咖啡！</p>
      </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
      <div class="qrcode">
        <canvas id="share-qrcode"></canvas>
        <p class="notice">扫描二维码，分享此文章</p>
      </div>
    
    <!-- 二维码 END -->
    
      <!-- Gitment START -->
      <div id="comments"></div>
      <!-- Gitment END -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
    <aside class="catalog-container">
  <div class="toc-main">
  <!-- 不蒜子统计 -->
    <strong class="toc-title">目录</strong>
    
      <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#python-asyncio"><span class="toc-nav-text">python asyncio</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#定义一个协程"><span class="toc-nav-text">定义一个协程</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#创建一个task"><span class="toc-nav-text">创建一个task</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#绑定回调"><span class="toc-nav-text">绑定回调</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#future-与-result"><span class="toc-nav-text">future 与 result</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#阻塞和await"><span class="toc-nav-text">阻塞和await</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#并发和并行"><span class="toc-nav-text">并发和并行</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#协程嵌套"><span class="toc-nav-text">协程嵌套</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#协程停止"><span class="toc-nav-text">协程停止</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#不同线程的事件循环"><span class="toc-nav-text">不同线程的事件循环</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#新线程协程"><span class="toc-nav-text">新线程协程</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#master-worker主从模式"><span class="toc-nav-text">master-worker主从模式</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#停止子线程"><span class="toc-nav-text">停止子线程</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#aiohttp"><span class="toc-nav-text">aiohttp</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#协程消费"><span class="toc-nav-text">协程消费</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#总结"><span class="toc-nav-text">总结</span></a></li></ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'http://www.liuqingzheng.top/python/Python异步IO并发/7-Python异步库之asyncio/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function() {
      $(this).attr('src', 'http://file.muyutech.com/error-img.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function() {
      var src = $(this).attr('src')
      if (src !== 'http://file.muyutech.com/error-img.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="'+ src +'" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function() {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


  <script>
    var qr = new QRious({
      element: document.getElementById('share-qrcode'),
      value: document.location.href
    });
  </script>



  <script>
    var gitmentConfig = "liuqingzheng";
    if (gitmentConfig !== 'undefined') {
      var gitment = new Gitment({
        id: "7-Python异步库之asyncio",
        owner: "liuqingzheng",
        repo: "FuckBlog",
        oauth: {
          client_id: "32a4076431cf39d0ecea",
          client_secret: "94484bd79b3346a949acb2fda3c8a76ce16990c6"
        },
        theme: {
          render(state, instance) {
            const container = document.createElement('div')
            container.lang = "en-US"
            container.className = 'gitment-container gitment-root-container'
            container.appendChild(instance.renderHeader(state, instance))
            container.appendChild(instance.renderEditor(state, instance))
            container.appendChild(instance.renderComments(state, instance))
            container.appendChild(instance.renderFooter(state, instance))
            return container;
          }
        }
      })
      gitment.render(document.getElementById('comments'))
    }
  </script>




    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
<!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
     本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
<span class="post-meta-divider">|</span>
<span id="busuanzi_container_site_uv" style='display:none'>
     本站访客数<span id="busuanzi_value_site_uv"></span>人
</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>



  <p class="copyright">
    &copy; 2021 | Proudly powered by <a href="https://www.cnblogs.com/xiaoyuanqujing" target="_blank">小猿取经</a>
    <br>
    Theme by <a href="https://www.cnblogs.com/xiaoyuanqujing" target="_blank" rel="noopener">小猿取经</a>
  </p>
</footer>

<script>
  function async(u, c) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async("//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js", function(){
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async("//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js", function(){
    $('figure pre').each(function(i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>





<!-- Baidu Tongji -->

<script>
    var _baId = 'c5fd96eee1193585be191f318c3fa725';
    // Originial
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?" + _baId;
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
</script>


<script src="/js/script.js"></script>


<script src="/js/search.js"></script>


<script src="/js/load.js"></script>



  <span class="local-search local-search-google local-search-plugin" style="right: 50px;top: 70px;;position:absolute;z-index:2;">
      <input type="search" placeholder="站内搜索" id="local-search-input" class="local-search-input-cls" style="">
      <div id="local-search-result" class="local-search-result-cls"></div>
  </span>


  </body>
</html>